home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / File / Passwd / Custom.php < prev    next >
PHP Script  |  2004-10-01  |  18KB  |  592 lines

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PEAR :: File :: Passwd :: Custom                                     |
  4. // +----------------------------------------------------------------------+
  5. // | This source file is subject to version 3.0 of the PHP license,       |
  6. // | that is available at http://www.php.net/license/3_0.txt              |
  7. // | If you did not receive a copy of the PHP license and are unable      |
  8. // | to obtain it through the world-wide-web, please send a note to       |
  9. // | license@php.net so we can mail you a copy immediately.               |
  10. // +----------------------------------------------------------------------+
  11. // | Copyright (c) 2003-2004 Michael Wallner <mike@iworks.at>             |
  12. // +----------------------------------------------------------------------+
  13. //
  14. // $Id: Custom.php,v 1.8 2004/03/14 14:28:12 mike Exp $
  15.  
  16. /**
  17. * Manipulate custom formatted passwd files
  18. * @author       Michael Wallner <mike@php.net>
  19. * @package      File_Passwd
  20. */
  21.  
  22. /**
  23. * Requires File::Passwd::Common
  24. */
  25. require_once 'File/Passwd/Common.php';
  26.  
  27. /** 
  28. * Manipulate custom formatted passwd files
  29. *
  30. * Usage Example:
  31. * <code>
  32. * $cust = &File_Passwd::factory('Custom');
  33. * $cust->setDelim('|');
  34. * $cust->load();
  35. * $cust->setEncFunc(array('File_Passwd', 'crypt_apr_md5'));
  36. * $cust->addUser('mike', 'pass');
  37. * $cust->save();
  38. * </code>
  39. * @author   Michael Wallner <mike@php.net>
  40. * @version  $Revision: 1.8 $
  41. * @access   public
  42. */
  43. class File_Passwd_Custom extends File_Passwd_Common
  44. {
  45.     /**
  46.     * Delimiter
  47.     *
  48.     * @access   private
  49.     * @var      string
  50.     */
  51.     var $_delim = ':';
  52.     
  53.     /**
  54.     * Encryption function
  55.     *
  56.     * @access   private
  57.     * @var      string
  58.     */
  59.     var $_enc = array('File_Passwd', 'crypt_md5');
  60.     
  61.     /**
  62.     * 'name map'
  63.     *
  64.     * @access   private
  65.     * @var      array
  66.     */
  67.     var $_map = array();
  68.     
  69.     /**
  70.     * Whether to use the 'name map' or not
  71.     *
  72.     * @var      boolean
  73.     * @access   private
  74.     */
  75.     var $_usemap = false;
  76.  
  77.     /**
  78.     * Constructor
  79.     *
  80.     * @access   protected
  81.     * @return   object
  82.     */
  83.     function File_Passwd_Custom($file = 'passwd')
  84.     {
  85.         $this->__construct($file);
  86.     }
  87.  
  88.     /**
  89.     * Fast authentication of a certain user
  90.     * 
  91.     * Returns a PEAR_Error if:
  92.     *   o file doesn't exist
  93.     *   o file couldn't be opened in read mode
  94.     *   o file couldn't be locked exclusively
  95.     *   o file couldn't be unlocked (only if auth fails)
  96.     *   o file couldn't be closed (only if auth fails)
  97.     *   o invalid encryption function <var>$opts[0]</var>,
  98.     *     or no delimiter character <var>$opts[1]</var> was provided
  99.     *
  100.     * @throws   PEAR_Error  FILE_PASSWD_E_UNDEFINED |
  101.     *                       FILE_PASSWD_E_FILE_NOT_OPENED |
  102.     *                       FILE_PASSWD_E_FILE_NOT_LOCKED |
  103.     *                       FILE_PASSWD_E_FILE_NOT_UNLOCKED |
  104.     *                       FILE_PASSWD_E_FILE_NOT_CLOSED |
  105.     *                       FILE_PASSWD_E_INVALID_ENC_MODE
  106.     * @static   call this method statically for a reasonable fast authentication
  107.     * @access   public
  108.     * @return   mixed   Returns &true; if authenticated, &false; if not or 
  109.     *                   <classname>PEAR_Error</classname> on failure.
  110.     * @param    string  $file   path to passwd file
  111.     * @param    string  $user   user to authenticate
  112.     * @param    string  $pass   plaintext password
  113.     * @param    array   $otps   encryption function and delimiter charachter
  114.     *                           (in this order)
  115.     */
  116.     function staticAuth($file, $user, $pass, $opts)
  117.     {
  118.         setType($opts, 'array');
  119.         if (count($opts) != 2 || empty($opts[1])) {
  120.             return PEAR::raiseError('Insufficient options.', 0);
  121.         }
  122.         
  123.         $line = File_Passwd_Common::_auth($file, $user);
  124.         
  125.         if (!$line || PEAR::isError($line)) {
  126.             return $line;
  127.         }
  128.         
  129.         list(,$real)= explode($opts[1], $line);
  130.         $crypted    = File_Passwd_Custom::_genPass($pass, $real, $opts[0]);
  131.         
  132.         if (PEAR::isError($crypted)) {
  133.             return $crypted;
  134.         }
  135.         
  136.         return ($crypted === $real);
  137.     }
  138.  
  139.     /**
  140.     * Set delimiter
  141.     * 
  142.     * You can set a custom char to delimit the columns of a data set.
  143.     * Defaults to a colon (':'). Be aware that this char mustn't be
  144.     * in the values of your data sets.
  145.     *
  146.     * @access   public
  147.     * @return   void
  148.     * @param    string  $delim  custom delimiting character
  149.     */
  150.     function setDelim($delim = ':')
  151.     {
  152.         @setType($delim, 'string');
  153.         if (empty($delim)) {
  154.             $this->_delim = ':';
  155.         } else {
  156.             $this->_delim = $delim{0};
  157.         }
  158.     }
  159.     
  160.     /**
  161.     * Get custom delimiter
  162.     *
  163.     * @access   public
  164.     * @return   string
  165.     */
  166.     function getDelim()
  167.     {
  168.         return $this->_delim;
  169.     }
  170.     
  171.     /**
  172.     * Set encryption function
  173.     *
  174.     * You can set a custom encryption function to use.
  175.     * The supplied function will be called by php's call_user_function(), 
  176.     * so you can supply an array with a method of a class/object, too 
  177.     * (i.e. array('File_Passwd', 'crypt_apr_md5').
  178.     * 
  179.     * 
  180.     * @throws   PEAR_Error          FILE_PASSWD_E_INVALID_ENC_MODE
  181.     * @access   public
  182.     * @return   mixed   Returns &true; on success or 
  183.     *                   <classname>PEAR_Error</classname> on failure.
  184.     * @param    mixed   $function    callable encryption function
  185.     */
  186.     function setEncFunc($function = array('File_Passwd', 'crypt_md5'))
  187.     {
  188.         if (!is_callable($function)) {
  189.             if (is_array($function)) {
  190.                 $function = implode('::', $function);
  191.             }
  192.             return PEAR::raiseError(
  193.                 sprintf(FILE_PASSWD_E_INVALID_ENC_MODE_STR, $function),
  194.                 FILE_PASSWD_E_INVALID_ENC_MODE
  195.             );
  196.         }
  197.         
  198.         $this->_enc = $function;
  199.         return true;
  200.     }
  201.     
  202.     /**
  203.     * Get current custom encryption method
  204.     *
  205.     * Possible return values (examples):
  206.     *   o 'md5'
  207.     *   o 'File_Passwd::crypt_md5'
  208.     * 
  209.     * @access   public
  210.     * @return   string
  211.     */
  212.     function getEncFunc()
  213.     {
  214.         if (is_array($this->_enc)) {
  215.             return implode('::', $this->_enc);
  216.         }
  217.         return $this->_enc;
  218.     }
  219.     
  220.     /**
  221.     * Whether to use the 'name map' of the extra properties or not
  222.     * 
  223.     * @see      File_Passwd_Custom::useMap()
  224.     * @see      setMap()
  225.     * @see      getMap()
  226.     * 
  227.     * @access   public
  228.     * @return   boolean always true if you set a value (true/false) OR
  229.     *                   the actual value if called without param
  230.     * 
  231.     * @param    boolean $bool   whether to use the 'name map' or not
  232.     */
  233.     function useMap($bool = null)
  234.     {
  235.         if (is_null($bool)) {
  236.             return $this->_usemap;
  237.         }
  238.         $this->_usemap = (bool) $bool;
  239.         return true;
  240.     }
  241.     
  242.     /**
  243.     * Set the 'name map' to use with the extra properties of the user
  244.     * 
  245.     * This map is used for naming the associative array of the extra properties.
  246.     *
  247.     * Returns a PEAR_Error if <var>$map</var> was not of type array.
  248.     * 
  249.     * @see      getMap()
  250.     * @see      useMap()
  251.     * 
  252.     * @throws   PEAR_Error  FILE_PASSWD_E_PARAM_MUST_BE_ARRAY
  253.     * @access   public
  254.     * @return   mixed       true on success or PEAR_Error
  255.     */
  256.     function setMap($map = array())
  257.     {
  258.         if (!is_array($map)) {
  259.             return PEAR::raiseError(
  260.                 sprintf(FILE_PASSWD_E_PARAM_MUST_BE_ARRAY_STR, '$map'),
  261.                 FILE_PASSWD_E_PARAM_MUST_BE_ARRAY
  262.             );
  263.         }
  264.         $this->_map = $map;
  265.         return true;
  266.     }
  267.     
  268.     /**
  269.     * Get the 'name map' which is used for the extra properties of the user
  270.     *
  271.     * @see      setMap()
  272.     * @see      useMap()
  273.     * 
  274.     * @access   public
  275.     * @return   array
  276.     */
  277.     function getMap()
  278.     {
  279.         return $this->_map;
  280.     }
  281.  
  282.     /**
  283.     * Apply changes an rewrite passwd file
  284.     *
  285.     * Returns a PEAR_Error if:
  286.     *   o directory in which the file should reside couldn't be created
  287.     *   o file couldn't be opened in write mode
  288.     *   o file couldn't be locked exclusively
  289.     *   o file couldn't be unlocked
  290.     *   o file couldn't be closed
  291.     * 
  292.     * @throws   PEAR_Error  FILE_PASSWD_E_FILE_NOT_OPENED |
  293.     *                       FILE_PASSWD_E_FILE_NOT_LOCKED |
  294.     *                       FILE_PASSWD_E_FILE_NOT_UNLOCKED |
  295.     *                       FILE_PASSWD_E_FILE_NOT_CLOSED
  296.     * @access   public
  297.     * @return   mixed   Returns &true; on success or 
  298.     *                   <classname>PEAR_Error</classname> on failure.
  299.     */
  300.     function save()
  301.     {
  302.         $content = '';
  303.         foreach ($this->_users as $user => $array){
  304.             $pass   = array_shift($array);
  305.             $extra  = implode($this->_delim, $array);
  306.             $content .= $user . $this->_delim . $pass;
  307.             if (!empty($extra)) {
  308.                 $content .= $this->_delim . $extra;
  309.             }
  310.             $content .= "\n";
  311.         }
  312.         return $this->_save($content);
  313.     }
  314.  
  315.     /**
  316.     * Parse the Custom password file
  317.     *
  318.     * Returns a PEAR_Error if passwd file has invalid format.
  319.     * 
  320.     * @throws   PEAR_Error  FILE_PASSWD_E_INVALID_FORMAT
  321.     * @access   public
  322.     * @return   mixed   Returns &true; on success or
  323.     *                   <classname>PEAR_Error</classname> on failure.
  324.     */
  325.     function parse()
  326.     {
  327.         $this->_users = array();
  328.         foreach ($this->_contents as $line){
  329.             $parts = explode($this->_delim, $line);
  330.             if (count($parts) < 2) {
  331.                 return PEAR::raiseError(
  332.                     FILE_PASSWD_E_INVALID_FORMAT_STR,
  333.                     FILE_PASSWD_E_INVALID_FORMAT
  334.                 );
  335.             }
  336.             $user = array_shift($parts);
  337.             $pass = array_shift($parts);
  338.             $values = array();
  339.             if ($this->_usemap) {
  340.                 $values['pass'] = $pass;
  341.                 foreach ($parts as $i => $value){
  342.                     if (isset($this->_map[$i])) {
  343.                         $values[$this->_map[$i]] = $value;
  344.                     } else {
  345.                         $values[$i+1] = $value;
  346.                     }
  347.                 }
  348.             } else {
  349.                 $values = array_merge(array($pass), $parts);
  350.             }
  351.             $this->_users[$user] = $values;
  352.             
  353.         }
  354.         $this->_contents = array();
  355.         return true;
  356.     }
  357.  
  358.     /**
  359.     * Add an user
  360.     *
  361.     * The username must start with an alphabetical character and must NOT
  362.     * contain any other characters than alphanumerics, the underline and dash.
  363.     * 
  364.     * If you use the 'name map' you should also use these naming in
  365.     * the supplied extra array, because your values would get mixed up
  366.     * if they are in the wrong order, which is always true if you
  367.     * DON'T use the 'name map'!
  368.     * 
  369.     * So be warned and USE the 'name map'!
  370.     * 
  371.     * Returns a PEAR_Error if:
  372.     *   o user already exists
  373.     *   o user contains illegal characters
  374.     *   o encryption mode is not supported
  375.     *   o any element of the <var>$extra</var> array contains the delimiter char
  376.     * 
  377.     * @throws   PEAR_Error  FILE_PASSWD_E_EXISTS_ALREADY |
  378.     *                       FILE_PASSWD_E_INVALID_ENC_MODE |
  379.     *                       FILE_PASSWD_E_INVALID_CHARS
  380.     * @access   public
  381.     * @return   mixed   Returns &true; on success or 
  382.     *                   <classname>PEAR_Error</classname> on failure.
  383.     * @param    string  $user   the name of the user to add
  384.     * @param    string  $pass   the password of the user to add
  385.     * @param    array   $extra  extra properties of user to add
  386.     */
  387.     function addUser($user, $pass, $extra = array())
  388.     {
  389.         if ($this->userExists($user)) {
  390.             return PEAR::raiseError(
  391.                 sprintf(FILE_PASSWD_E_EXISTS_ALREADY_STR, 'User ', $user),
  392.                 FILE_PASSWD_E_EXISTS_ALREADY
  393.             );
  394.         }
  395.         if (!preg_match($this->_pcre, $user) || strstr($user, $this->_delim)) {
  396.             return PEAR::raiseError(
  397.                 sprintf(FILE_PASSWD_E_INVALID_CHARS_STR, 'User ', $user),
  398.                 FILE_PASSWD_E_INVALID_CHARS
  399.             );
  400.         }
  401.         if (!is_array($extra)) {
  402.             setType($extra, 'array');
  403.         }
  404.         foreach ($extra as $e){
  405.             if (strstr($e, $this->_delim)) {
  406.                 return PEAR::raiseError(
  407.                     sprintf(FILE_PASSWD_E_INVALID_CHARS_STR, 'Property ', $e),
  408.                     FILE_PASSWD_E_INVALID_CHARS
  409.                 );
  410.             }
  411.         }
  412.         
  413.         $pass = $this->_genPass($pass);
  414.         if (PEAR::isError($pass)) {
  415.             return $pass;
  416.         }
  417.         
  418.         /**
  419.         * If you don't use the 'name map' the user array will be numeric.
  420.         */
  421.         if (!$this->_usemap) {
  422.             array_unshift($extra, $pass);
  423.             $this->_users[$user] = $extra;
  424.         } else {
  425.             $map = $this->_map;
  426.             array_unshift($map, 'pass');
  427.             $extra['pass'] = $pass;
  428.             foreach ($map as $key){
  429.                 $this->_users[$user][$key] = @$extra[$key];
  430.             }
  431.         }
  432.         
  433.         return true;
  434.     }
  435.  
  436.     /**
  437.     * Modify properties of a certain user
  438.     *
  439.     * # DON'T MODIFY THE PASSWORD WITH THIS METHOD!
  440.     * 
  441.     * You should use this method only if the 'name map' is used, too.
  442.     * 
  443.     * Returns a PEAR_Error if:
  444.     *   o user doesn't exist
  445.     *   o any property contains the custom delimiter character
  446.     * 
  447.     * @see      changePasswd()
  448.     * 
  449.     * @throws   PEAR_Error  FILE_PASSWD_E_EXISTS_NOT | 
  450.     *                       FILE_PASSWD_E_INVALID_CHARS
  451.     * @access   public
  452.     * @return   mixed       true on success or PEAR_Error
  453.     * @param    string      $user           the user to modify
  454.     * @param    array       $properties     an associative array of 
  455.     *                                       properties to modify
  456.     */
  457.     function modUser($user, $properties = array())
  458.     {
  459.         if (!$this->userExists($user)) {
  460.             return PEAR::raiseError(
  461.                 sprintf(FILE_PASSWD_E_EXISTS_NOT_STR, 'User ', $user),
  462.                 FILE_PASSWD_E_EXISTS_NOT
  463.             );
  464.         }
  465.         
  466.         if (!is_array($properties)) {
  467.             setType($properties, 'array');
  468.         }
  469.         
  470.         foreach ($properties as $key => $value){
  471.             if (strstr($value, $this->_delim)) {
  472.                 return PEAR::raiseError(
  473.                     sprintf(FILE_PASSWD_E_INVALID_CHARS_STR, 'User ', $user),
  474.                     FILE_PASSWD_E_INVALID_CHARS
  475.                 );
  476.             }
  477.             $this->_users[$user][$key] = $value;
  478.         }
  479.         
  480.         return true;
  481.     }
  482.  
  483.     /**
  484.     * Change the password of a certain user
  485.     *
  486.     * Returns a PEAR_Error if:
  487.     *   o user doesn't exists
  488.     *   o encryption mode is not supported
  489.     * 
  490.     * @throws   PEAR_Error  FILE_PASSWD_E_EXISTS_NOT |
  491.     *                       FILE_PASSWD_E_INVALID_ENC_MODE
  492.     * @access   public
  493.     * @return   mixed   Returns &true; on success or 
  494.     *                   <classname>PEAR_Error</classname> on failure.
  495.     * @param    string  $user   the user whose password should be changed
  496.     * @param    string  $pass   the new plaintext password
  497.     */
  498.     function changePasswd($user, $pass)
  499.     {
  500.         if (!$this->userExists($user)) {
  501.             return PEAR::raiseError(
  502.                 sprintf(FILE_PASSWD_E_EXISTS_NOT_STR, 'User ', $user),
  503.                 FILE_PASSWD_E_EXISTS_NOT
  504.             );
  505.         }
  506.         
  507.         $pass = $this->_genPass($pass);
  508.         if (PEAR::isError($pass)) {
  509.             return $pass;
  510.         }
  511.         
  512.         if ($this->_usemap) {
  513.             $this->_users[$user]['pass'] = $pass;
  514.         } else {
  515.             $this->_users[$user][0] = $pass;
  516.         }
  517.         
  518.         return true;
  519.     }
  520.  
  521.     /**
  522.     * Verify the password of a certain user
  523.     * 
  524.     * Returns a PEAR_Error if:
  525.     *   o user doesn't exist
  526.     *   o encryption mode is not supported
  527.     *
  528.     * @throws   PEAR_Error  FILE_PASSWD_E_EXISTS_NOT |
  529.     *                       FILE_PASSWD_E_INVALID_ENC_MODE
  530.     * @access   public
  531.     * @return   mixed   Returns &true; if passwors equal, &false; if they don't 
  532.     *                   or <classname>PEAR_Error</classname> on fialure.
  533.     * @param    string  $user   the user whose password should be verified
  534.     * @param    string  $pass   the password to verify
  535.     */
  536.     function verifyPasswd($user, $pass)
  537.     {
  538.         if (!$this->userExists($user)) {
  539.             return PEAR::raiseError(
  540.                 sprintf(FILE_PASSWD_E_EXISTS_NOT_STR, 'User ', $user),
  541.                 FILE_PASSWD_E_EXISTS_NOT
  542.             );
  543.         }
  544.         $real = 
  545.             $this->_usemap ? 
  546.             $this->_users[$user]['pass'] : 
  547.             $this->_users[$user][0]
  548.         ;
  549.         return ($real === $this->_genPass($pass, $real));
  550.     }
  551.     
  552.     /**
  553.     * Generate crypted password from the plaintext password
  554.     *
  555.     * Returns a PEAR_Error if actual encryption mode is not supported.
  556.     * 
  557.     * @throws   PEAR_Error  FILE_PASSWD_E_INVALID_ENC_MODE
  558.     * @access   private
  559.     * @return   mixed   Returns the crypted password or 
  560.     *                   <classname>PEAR_Error</classname>
  561.     * @param    string  $pass   the plaintext password
  562.     * @param    string  $salt   the crypted password from which to gain the salt
  563.     * @param    string  $func   the encryption function to use
  564.     */
  565.     function _genPass($pass, $salt = null, $func = null)
  566.     {
  567.         if (is_null($func)) {
  568.             $func = $this->_enc;
  569.         }
  570.         
  571.         if (!is_callable($func)) {
  572.             if (is_array($func)) {
  573.                 $func = implode('::', $func);
  574.             }
  575.             return PEAR::raiseError(
  576.                 sprintf(FILE_PASSWD_E_INVALID_ENC_MODE_STR, $func),
  577.                 FILE_PASSWD_E_INVALID_ENC_MODE
  578.             );
  579.         }
  580.         
  581.         $return = @call_user_func($func, $pass, $salt);
  582.         
  583.         if (is_null($return) || $return === false) {
  584.             $return = @call_user_func($func, $pass);
  585.         }
  586.         
  587.         return $return;
  588.     }
  589. }
  590. ?>